package ui;

import java.awt.Frame;
import java.awt.Rectangle;
import java.awt.Font;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Component;
import java.awt.Event;
import java.awt.TextField;
import java.awt.List;
import java.awt.Choice;

/**
* Extends the standard AWT dialog to provide field validation.
* It uses Microsoft's DialogLayout manager to place the
* components.
*
* @version 1.0
* @author Hans U. Gerber (<a href="mailto:gerber@ifh.ee.ethz.ch">gerber@ifh.ee.ethz.ch</a>)
*/
public class Dialog extends java.awt.Dialog
{
	protected Frame parent;
	protected DialogLayout layout;
	private boolean shouldBeModal;

	public Dialog(Frame parent, Rectangle rect, String title, boolean modal) {
		//	Modal dialogs are broken in the AWT. A call to show() does not block
		//	on all platforms. As a quick fix, I disabled the parent frame when showing
		//	the dialog and re-enabled the parent when the dialog went away.
		//	This worked fine with Internet Explorer 3.0, even with a modal dialog
		//	open, the IE menu and toolbar were still active, the IE window could
		//	even be minimized and took the dialog with it.
		//	But in IE 3.01, MS obviously changed the behavior of modal dialogs:
		//	show() does still not block, but the IE window is now completely 
		//	disabled, including the system menu. What's worse, when my dialog goes
		//	away, the IE window is no longer active, but the next application window
		//	on the screen. I tried requestFocus(), but this produced nasty
		//	app window flickering.
		//	I observed that I can get back the old behavior if I create all dialogs
		//	non-modal and then disable/enable the parent explicitely.

		//	Make dialog non-modal (parent will be disabled explicitely in show());
		//	remember the state of the modal flag:
		super(parent, title, false);
		shouldBeModal = modal;
		this.parent = parent;
		//	Since a given font may not be supported across all platforms, it
		//	is safe to modify only the size of the font, not the typeface.
	  Font OldFnt = getFont();
	  if (OldFnt != null) {
		Font NewFnt = new Font(OldFnt.getName(), OldFnt.getStyle(), 8);
    	setFont(NewFnt);
	  }
		//	All position and sizes are in dialog logical units, so we use a
		//	DialogLayout as our layout manager.
		layout = new DialogLayout(this, rect.width, rect.height);
		setLayout(layout);
		addNotify();
		Dimension size = layout.getDialogSize();
		Insets insets = insets();
		resize(insets.left + size.width  + insets.right,
				insets.top  + size.height + insets.bottom);
		move(rect.x, rect.y);
	}

	/**
  * Finds the top-level frame in the ancestry of startComponent,
	* if there is one.
  */
  public static Frame findFirstFrame(Component startComponent) {
		Component c = startComponent;
		while (c!=null) {
			if (c instanceof Frame) break;
			c = c.getParent();
		}
		return (Frame)c;
	}

	/**
	* This method is overridden just to close the dialog
	* window on receiving the WINDOW_DESTROY message.
	* All other messages are passed along unhandled.
	*/
	public boolean handleEvent(Event evt)	{
		switch (evt.id)	{
			case Event.WINDOW_DESTROY:
				onCancel();
				return true;
			default:
				return super.handleEvent(evt);
		}			 
	}

	/**
	* Adds a component to the dialog, shaping it at the same time.
	*/
	public Component addComponent(Component component, Rectangle rect) {
		Component retVal = super.add(component);
		layout.setShape(component, rect);
		return retVal;
	}

	/**
	* Called to validate and accept the contents of the dialog's components 
	* when the user selects "OK" or "Apply" in the dialog. If all contents
	* are found valid, their values are transferred to the components' data
	*	models. If the dialog is modal, it goes away after this.
	* @return <code>null</code> if all validatable components are
	*		valid, an error message otherwise
	*/
	public String onApply() {
		//	First, check if all validatable components
		//	are valid; but do not yet accept any data.
		//  Only after all components	have been checked
		//  and their values found valid then accept them.
		String validateString = check();
		if (validateString == null) {
			accept();
			if (shouldBeModal) {
				dispose();
			}
		}
		return validateString;
	}

	/**
	* To be called when the user leaves the dialog with "Cancel".
	*/
	public void onCancel() {
		dispose();
	}

	/**
	* Checks the validity of the contents of all validatable components
	* in the dialog.
	* If any component is found invalid, it receives the focus and
	* the error message of the offending component is returned.
	* @return <code>null</code> if all components are valid, an error
	*		message otherwise
	*/
	public String check() {
		String validateMsg = null;
		for (int i = 0; i < countComponents(); i++) {
			Component component = getComponent(i);
			if (component instanceof Validatable) {
				validateMsg = ((Validatable)component).check();
				if (validateMsg != null) {
					component.requestFocus();
					if (component instanceof TextField) {
						((TextField)component).selectAll();
					}
					return validateMsg;
				}
			}
		}
		return null;
	}

	/**
	* Accepts the contents from all validatable components in the dialog 
	* and updates their data models.
	*/
	public void accept() {
		for (int i = 0; i < countComponents(); i++) {
			Component component = getComponent(i);
			if (component instanceof Validatable) {
				Validatable field = (Validatable)component;
				field.accept();
			}
		}
	}

	/**
	* Just a fix for an AWT bug: The Windows implementation
	* does not handle modal dialogs. As a quick workaround,
	* I disable the parent window as long as the dialog
	* is shown.
	*/
	public void show() {
		if (shouldBeModal) {
			parent.disable();
		}
		super.show();
	}

	/**
	* A fix for an AWT bug: The Windows implementation
	* does not handle modal dialogs. As a quick workaround,
	* I disable the parent window as long as the dialog
	* is shown. I have to reenable the parent here.
  */
	public void dispose() {
		super.dispose();
		if (shouldBeModal) {
			parent.enable();
		}
	}
}
